#ifndef __ADD__HPP__
#define __ADD__HPP__

// Add
struct Add {
  static std::string name() { return "ADD"; }
  constexpr static int apply (int l, int h) {
    return l + h; 
  }
};

// template<template <class> class AA, class A, template <class> class BB, class B>
// constexpr Expression<BinaryExpression<AA<A>, BB<B>, Add> >
// operator+(AA<A> const & a, BB<B> const & b)
// {
//   typedef BinaryExpression<AA<A>, BB<B>, Add>  ExprT;
//   return Expression<ExprT>(ExprT(a, b));
// }

// template<template <class> class AA, class A, class B>
// constexpr Expression<BinaryExpression<AA<A>, B, Add> >
// operator+(AA<A> const & a, B const & b)
// {
//   typedef BinaryExpression<AA<A>, B, Add>  ExprT;
//   return Expression<ExprT>(ExprT(a, b));
// }

// template<class A, template <class> class BB, class B>
// constexpr Expression<BinaryExpression<A, BB<B>, Add> >
// operator+(A const & a, BB<B> const & b)
// {
//   typedef BinaryExpression<A, BB<B>, Add>  ExprT;
//   return Expression<ExprT>(ExprT(a, b));
// }

// template<class A, class B>
// constexpr Expression<BinaryExpression<A, B, Add> >
// operator+(A const & a, B const & b)
// {
//   typedef BinaryExpression<A, B, Add>  ExprT;
//   return Expression<ExprT>(ExprT(a, b));
// }


template< class A, class B>
constexpr Expression<BinaryExpression<Expression<A>, Expression<B>, Add>>
  operator+(const Expression<A> &a, const Expression<B> &b)
{
  typedef BinaryExpression <Expression<A>, Expression<B>, Add> ExprT;
  return Expression<ExprT>(ExprT(a,b));
}

// Can we do without this ? Specializations don't overload !
template <class A, int PLACE>
constexpr Expression<BinaryExpression<Expression<A>, VariableTerm<PLACE>, Add>>
  operator+(const Expression<A> &a, const VariableTerm<PLACE> &b)
{
  typedef BinaryExpression <Expression<A>, VariableTerm<PLACE>, Add> ExprT;
  return Expression<ExprT>(ExprT(a,b));
}

// Can we do without this ? Specializations don't overload !
template <class B, int PLACE>
constexpr Expression<BinaryExpression<VariableTerm<PLACE>, Expression<B>, Add>>
operator+(const VariableTerm<PLACE> &a, const Expression<B> &b)
{
  typedef BinaryExpression <VariableTerm<PLACE>, Expression<B>, Add> ExprT;
  return Expression<ExprT>(ExprT(a,b));
}

// Can we do without this ? Specializations don't overload !
template <int PLACEA, int PLACEB>
constexpr Expression<BinaryExpression<VariableTerm<PLACEA>, VariableTerm<PLACEB>, Add>>
  operator+(const VariableTerm<PLACEA> &a, const VariableTerm<PLACEB> &b)
{
  typedef BinaryExpression <VariableTerm<PLACEA>, VariableTerm<PLACEB>, Add> ExprT;
  return Expression<ExprT>(ExprT(a,b));
}

// Can we do without this ? Specializations don't overload !
template <class A>
constexpr Expression<BinaryExpression<Expression<A>, ConstantTerm, Add>>
  operator+(const Expression<A> &a, const int &b)
{
  typedef BinaryExpression <Expression<A>, ConstantTerm, Add> ExprT;
  return Expression<ExprT>(ExprT(a,ConstantTerm(b)));
}

// Can we do without this ? Specializations don't overload !
template <class B>
constexpr Expression<BinaryExpression<ConstantTerm, Expression<B>, Add>>
  operator+(const int &a, const Expression<B> &b)
{
  typedef BinaryExpression <ConstantTerm, Expression<B>, Add> ExprT;
  return Expression<ExprT>(ExprT(ConstantTerm(a),b));
}


// template<int PLACEA, int PLACEB>
// constexpr Expression<BinaryExpression<Expression<VariableTerm<PLACEA> >, Expression<VariableTerm<PLACEB> >, Add> >
// operator+(VariableTerm<PLACEA> const & a, VariableTerm<PLACEB> const & b) {
//   typedef BinaryExpression <Expression<VariableTerm<PLACEA> >, Expression<VariableTerm<PLACEB> >, Add> ExprT;
//   return Expression<ExprT>(ExprT(Expression<VariableTerm<PLACEA> >(a),Expression<VariableTerm<PLACEB> >(b)));
// }

// template<int PLACE>
// constexpr Expression<BinaryExpression<Expression<ConstantTerm>, Expression<VariableTerm<PLACE> >, Add> >
// operator+(ConstantTerm const & a, VariableTerm<PLACE> const & b) {
//   typedef BinaryExpression <Expression<ConstantTerm>, Expression<VariableTerm<PLACE> >, Add> ExprT;
//   return Expression<ExprT>(ExprT(Expression<ConstantTerm>(a),Expression<VariableTerm<PLACE> >(b)));
// }

template<int PLACE>
constexpr Expression<BinaryExpression<Expression<VariableTerm<PLACE> >, Expression<ConstantTerm>, Add> >
operator+(VariableTerm<PLACE> const & b, ConstantTerm const & a) {
  typedef BinaryExpression <Expression<VariableTerm<PLACE> >, Expression<ConstantTerm>, Add> ExprT;
  return Expression<ExprT>(ExprT(Expression<VariableTerm<PLACE> >(b), Expression<ConstantTerm>(a)));
}

constexpr Expression<BinaryExpression<Expression<ConstantTerm>, Expression<ConstantTerm>, Add> >
operator+(ConstantTerm const & b, ConstantTerm const & a) {
  typedef BinaryExpression <Expression<ConstantTerm>, Expression<ConstantTerm>, Add> ExprT;
  return Expression<ExprT>(ExprT(Expression<ConstantTerm>(b), Expression<ConstantTerm>(a)));
}

#endif //__ADD__HPP__
